package wrm.saferJava.annotations.state;


import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.AdviceAdapter;

import wrm.saferJava.aop.GuardClassInjector;
import wrm.saferJava.oval.exception.ConstraintsViolatedException;
import wrm.saferJava.oval.guard.Guarded;

public class StateGuardInjector extends GuardClassInjector 
{
	

	private final static String ADVICE_STATE_QUALIFIER = "$$SF$$PROTOCOLL$$STATE";
	private final Integer startState = 0;
	
	
	public StateGuardInjector(ClassVisitor arg0, ClassLoader loader) {
		super(arg0, loader);
	}

	
	
	@Override
	public void visitEnd() {
		//insert new state variable
		if (isGuardedClass())
		{
			FieldVisitor fv = visitField(ACC_PRIVATE, ADVICE_STATE_QUALIFIER, "I", null, startState);
			fv.visitEnd();
			
		}
	}
	
	
	
	@Override
	public MethodVisitor visitMethod(int access, String name, String descriptor,
			String signature, String[] throwing) {
		
		MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, throwing);
		if (isGuardedClass() && ((access & ACC_ABSTRACT) == 0))
			mv = new CheckInjector(mv, access, name, descriptor, getCurClass());
		
		
		
		
		
		return mv;
	}
	
	
	
	public class CheckInjector extends AdviceAdapter implements AnnotationVisitor
	{

		private String owner;
		private boolean annotationFound = false;
		private int stateToCheck;
		private Integer nextState = -1;
		private String methodName; 

		protected CheckInjector(MethodVisitor arg0, int arg1, String arg2,
				String arg3, String owner) {
			super(arg0, arg1, arg2, arg3);
			methodName = arg2;
			this.owner = owner;
		}
		
		@Override
		public AnnotationVisitor visitAnnotation(String arg0, boolean arg1) {
			if (arg0.equals(Type.getDescriptor(AllowedCallState.class)))
			{
				annotationFound=true;
				return this;
			}
			return super.visitAnnotation(arg0, arg1);
		}
		
		@Override
		protected void onMethodEnter()
		{
			if (!annotationFound)
				return;
			
			String exceptiontype = Type.getInternalName(ProtocolException.class);
			mv.visitVarInsn(ALOAD, 0);
			mv.visitFieldInsn(GETFIELD, getCurClass(), ADVICE_STATE_QUALIFIER, "I");
			mv.visitIntInsn(BIPUSH, stateToCheck);
			Label l0 = new Label();
			mv.visitJumpInsn(IF_ICMPEQ, l0);
			mv.visitTypeInsn(NEW, exceptiontype);
			mv.visitInsn(DUP);
			mv.visitLdcInsn("Statecheck failed: Method <" + methodName + "> not allowed in current State.");
			mv.visitMethodInsn(INVOKESPECIAL, exceptiontype, "<init>", "(Ljava/lang/String;)V");
			mv.visitInsn(ATHROW);
			mv.visitLabel(l0);
			
			//increment state
			if (nextState == -1)
				nextState = stateToCheck+1;
			if (stateToCheck != nextState)
			{
				mv.visitVarInsn(ALOAD, 0);
				mv.visitIntInsn(BIPUSH, nextState);
				mv.visitFieldInsn(PUTFIELD, getCurClass(), ADVICE_STATE_QUALIFIER, "I");
			}
		}

		public void visit(String arg0, Object arg1) {
			if (arg0.equals("value"))
				stateToCheck = ((Integer)arg1); 
			if (arg0.equals("next"))
				nextState = ((Integer)arg1);
			
			
		}

		public AnnotationVisitor visitAnnotation(String arg0, String arg1) {
			// TODO Auto-generated method stub
			return null;
		}

		@Override
		public AnnotationVisitor visitArray(String arg0) {
			// TODO Auto-generated method stub
			return null;
		}

		@Override
		public void visitEnum(String arg0, String arg1, String arg2) {
			// TODO Auto-generated method stub
			
		}
		
		
		
		
		
	}
	
	
	
}
